package kino.net;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import kino.client.KinoClient;
import kino.server.KinoServer;

public class KinoServerHandler extends ChannelHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        KinoServer kinoServer = (KinoServer) ctx.attr(AttributeKey.valueOf("kinoServer")).get();
        kinoServer.addClient(new KinoClient(kinoServer.defaultDb(), ctx.channel(), kinoServer.getCommandInterpreter()));
        super.channelActive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        KinoServer kinoServer = (KinoServer) ctx.attr(AttributeKey.valueOf("kinoServer")).get();

        String input;
        ByteBuf byteBuf;
        RedisFrame frame = null;
        // 1. 转换msg转换为帧
        if (msg instanceof RedisFrame) {
            frame = (RedisFrame) msg;
            input = frame.argsToInputLine();
        } else {
            byteBuf = (ByteBuf) msg;
            input = byteBuf.toString(CharsetUtil.UTF_8);
        }

        // 2. 把命令塞进线程池执行是为了让netty的多线程变成单线程执行，redis只用单线程呢所以少了很多指令顺序性的问题。
        //    在线程池里执行命令，该线程池但线程且有公平临界资源控制，保障原子性
        RedisFrame finalFrame = frame;
        kinoServer.execute(() -> {
            // 设置原始命令内容用于保存aof
            finalFrame.getClient().setQuerybuf(finalFrame.getOriQueryBytes());
            // 传递命令行
            finalFrame.getClient().queryBufferAppend(input);
            // 执行命令
            finalFrame.getClient().execute();
        });
    }

    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        KinoServer kinoServer = (KinoServer) ctx.attr(AttributeKey.valueOf("kinoServer")).get();
        // 1. 获取kinoClinet
        KinoClient kinoClient = kinoServer.getKinoClientByChannel(ctx.channel());
        // 2. 连接注销
        kinoServer.clientLoginOut(kinoClient);
        super.close(ctx, promise);
    }
}